<?php 
/*
只有has具有级联的属性
*/
class Model
{
	protected   $data;
	protected   $table;
	protected   $prefix;
	protected   $db;
	/*
	例如:member 包含member_info
	member_info为对应的model名，refcolumn为member_info里的对应字段，own_column为本model的对应字段
	四种级联方式：restrict,cascade,set null,no action
	如果为no action的话，可以不设.
	$hasone=array("memeber_info"=>array("ref_column"=>"member_id","own_column"=>"id","ondelete"=>"delete"))
	*/
	protected   $hasone=array();
	/*
	例如：大类包含多个子类
	subcategory为对应的model名，parent_id为subcategory里对应本model的id
	$hasmany=array("subcategory"=>array("ref_column"=>"parent_id","own_column"=>"id"))
	*/
	protected 	$hasmany=array();
	/*
	例如member_info 属于member
	member为对应的model名，id为member里对应本model的member_id
	$belongsto_one=array("member"=>array("ref_column"=>"id","own_column"=>"member_id"))
	*/
	protected 	$belongsto_one=array();
	/*
	例如：一个学科可以被多个学生选择
	student为对应的model名，id为student里对应本model的student_id
	$belongsto_many=array("student"=>array("ref_column"=>"id","own_column"=>"student_id"))
	*/
	protected 	$belongsto_many=array();
	
	
	protected $hasone_model=array();
	protected $hasmany_model=array();
	protected $belongsto_one_model=array();
	protected $belongsto_many_model=array();
	public function __construct($prefix="")
	{
		if(!empty($prefix))
		{
			$this->prefix=$prefix;
		}
		$registry=Registry::getInstance();
		$this->db=$registry->get("db");
	}
	
	
	public  function __set($key,$value)
	{
		$this->data[$key]=$value;
	}
	
	public function __get($key)
	{
		if(array_key_exists($key,$this->data))
		{
			return $this->data[$key];
		}
		else 
		{
			return null;
		}
	}
	
	public function __isset($key)
	{
		return array_key_exists($key,$this->data);
	}
	
	public function __unset($key)
	{
		unset($this->data[$key]);
	}
	
	public function save()
	{
		if(array_key_exists("id",$this->data))
		{
			return $this->update();
		}
		else 
		{
			return !empty($this->data["id"]);
		}
	}
	
	public function insert()
	{
		$check=$this->checkInsert();
		if($check!==true)
		{
			return $check;
		}
		$table=$this->prefix.$this->table;
		$id=$this->db->insert($this->data,$table);
		$this->data["id"]=$id;
	}
	
	public function delete($where="")
	{
		
		$table=$this->prefix.$this->table;
		$sql="delete from $table where ";
		if(empty($where))
		{
			$sql.=" id=".$this->data["id"];
			if(!$this->checkRestrict())
			{
				return false;
			}
			$check=$this->checkDelete();
			if($check!==true)
			{
				return $check;
			}
			$this->deleteHas();
		}
		else 
		{
			$sql.=$where;
		}
		$this->db->delete($sql);
	}
	
	public function update($where="")
	{
		$check=$this->checkSave();
		if($check!==true)
		{
			return $check;
		}
		$table=$this->prefix.$this->table;
		$data=$this->data;
		if(empty($where))
		{
			$where=" id=".$data["id"];
		}
		
		unset($data["id"]);
		
		return $this->db->update($data,$where,$table);
	}
	
	public function findById($id)
	{
		$table=$this->prefix.$this->table;
		$data=$this->db->select(" * from $table where id=$id");
		if(!empty($data))
		{
			$this->data=$data[0];
		}
	}
	
	public function find($where="",$count=false)
	{
		$table=$this->prefix.$this->table;
		$sql=" * from $table ";
		if(!empty($where))
		{
			$sql.=" where $where";
		}
		return $this->db->select($sql,$count);
	}
	
	public function findBySql($sql)
	{
		$sql=trim($sql);
		if(stripos($sql,"select")===0)
		{
			$sql=substr($sql,strlen("select"));
		}
		return $this->db->select($sql,true);
	}
	public function getLastFindTotal()
	{
		return $this->db->getLastFindTotal();
	}
	
	public function getHasOne($model)
	{
		if(!array_key_exists($model,$this->hasone))
		{
			return null;
		}
		if(array_key_exists($model,$this->hasone_model))
		{
			return $this->hasone_model[$model];
		}
		else 
		{
			if(!class_exists($model))
			{
				if(file_exists(MODELDIR.strtolower($model).".php"))
				{
					include(MODELDIR.strtolower($model).".php");
				}
				else 
				{
					throw new Exception("Not find model $model");
				}
			}
			$hasone=new $model();
			$hasone_column=$this->hasone[$model];
			$where=$hasone_column["ref_column"]."='".$this->data[$hasone_column["own_column"]]."'";
			$result=$hasone->find($where);
			if(!empty($result))
			{
				$row=$result[0];
				$hasone->setData($row);
			}
			$this->hasone_model[$model]=$hasone;
			return $hasone;
		}
	}
	
	public function getHasMany($model)
	{
		if(!array_key_exists($model,$this->hasmany))
		{
			return null;
		}
		if(array_key_exists($model,$this->hasmany_model))
		{
			return $this->hasmany_model[$model];
		}
		else 
		{
			if(!class_exists($model))
			{
				if(file_exists(MODELDIR.strtolower($model).".php"))
				{
					include(MODELDIR.strtolower($model).".php");
				}
				else 
				{
					throw new Exception("Not find model $model");
				}
			}
			$hasmany=new $model();
			$hasmany_column=$this->hasmany[$model];
			$where=$hasmany_column["ref_column"]."='".$this->data[$hasmany_column["own_column"]]."'";
			$result=$hasmany->find($where);
			$has_array=array();
			if(!empty($result))
			{
				foreach ($result as $row)
				{
					$hasmany=new $model();
					$hasmany->setData($row);
					$has_array[]=$hasmany;
				}
			}
			
			$this->hasmany_model[$model]=$has_array;
			return $has_array;
		}
	}
	
	public function getHasManyTotal($model)
	{
		if(!array_key_exists($model,$this->hasmany))
		{
			return null;
		}
		if(array_key_exists($model,$this->hasmany_model))
		{
			return count($this->hasmany_model[$model]);
		}
		else 
		{
			if(!class_exists($model))
			{
				if(file_exists(MODELDIR.strtolower($model).".php"))
				{
					include(MODELDIR.strtolower($model).".php");
				}
				else 
				{
					throw new Exception("Not find model $model");
				}
			}
			$hasmany=new $model();
			$hasmany_column=$this->hasmany[$model];
			$where=$hasmany_column["ref_column"]."='".$this->data[$hasmany_column["own_column"]]."'";
			$sql="select count(*) as c from ".$model->getTableName()." where ".$where;
			$result=$hasmany->findBySql($sql);
			$row=$result[0];
			$row=$row["c"];
			return $row;
		}
	}
	
	public function getBelongsToOne($model)
	{
		if(!array_key_exists($model,$this->belongsto_one))
		{
			return null;
		}
		if(array_key_exists($model,$this->belongsto_one_model))
		{
			return $this->belongsto_one_model[$model];
		}
		else 
		{
			if(!class_exists($model))
			{
				if(file_exists(MODELDIR.strtolower($model).".php"))
				{
					include(MODELDIR.strtolower($model).".php");
				}
				else 
				{
					throw new Exception("Not find model $model");
				}
			}
			$be_one=new $model();
			$be_one_column=$this->belongsto_one[$model];
			$where=$be_one_column["ref_column"]."='".$this->data[$be_one_column["own_column"]]."'";
			$result=$be_one->find($where);
			if(!empty($result))
			{
				$row=$result[0];
				$be_one->setData($row);
			}
			$this->belongsto_one_model[$model]=$be_one;
			return $be_one;
		}
	}
	
	public function getBelongsToMany($model)
	{
		if(!array_key_exists($model,$this->belongsto_many))
		{
			return null;
		}
		if(array_key_exists($model,$this->belongsto_many_model))
		{
			return $this->belongsto_many_model[$model];
		}
		else 
		{
			if(!class_exists($model))
			{
				if(file_exists(MODELDIR.strtolower($model).".php"))
				{
					include(MODELDIR.strtolower($model).".php");
				}
				else 
				{
					throw new Exception("Not find model $model");
				}
			}
			$be_many=new $model();
			$be_many_column=$this->belongsto_many[$model];
			$where=$be_many_column["ref_column"]."='".$this->data[$be_many_column["own_column"]]."'";
			$result=$be_many->find($where);
			$has_array=array();
			if(!empty($result))
			{
				foreach ($result as $row)
				{
					$be_many=new $model();
					$be_many->setData($row);
					$has_array[]=$be_many;
				}
			}
			$this->belongsto_many_model[$model]=$has_array;
			return $has_array;
		}
	}
	
	public function getBelongsToManyTotal($model)
	{
		if(!array_key_exists($model,$this->belongsto_many))
		{
			return null;
		}
		if(array_key_exists($model,$this->belongsto_many_model))
		{
			return count($this->belongsto_many_model[$model]);
		}
		else 
		{
			if(!class_exists($model))
			{
				if(file_exists(MODELDIR.strtolower($model).".php"))
				{
					include(MODELDIR.strtolower($model).".php");
				}
				else 
				{
					throw new Exception("Not find model $model");
				}
			}
			$be_many=new $model();
			$be_many_column=$this->belongsto_many[$model];
			$where=$be_many_column["ref_column"]."='".$this->data[$be_many_column["own_column"]]."'";
			$sql="select count(*) as c from ".$model->getTableName()." where $where";
			$result=$be_many->findBySql($sql);
			$row=$result[0];
			$row=$row["c"];
			return $row;
		}
	}
	
	public function getTableName()
	{
		return $this->prefix.$this->table;
	}
	
	public function setData($data)
	{
		$this->data=$data;
	}
	
	public function deleteHas()
	{
		if(!empty($this->hasone))
		{
			foreach ($this->hasone as $model=>$column)
			{
				if(empty($column["ondelete"])||$column["ondelete"]=="no action")
				{
					continue;
				}
				if(!class_exists($model))
				{
					include(MODELDIR.$model.".php");
				}
				$has=new $model();
				if($column["ondelete"]=="cascade")
				{									
					$where=$column["ref_column"]."='".$this->data[$column["own_column"]]."'";
					$has->delete($where);
				}
				else if($column["ondelete"]=="set null")
				{
					$sql.="update ".$model->getTableName()." set ".$column["ref_column"]."=null where ".$column["ref_column"]."='".$this->data[$column["own_column"]]."'";
					
					$this->db->execute($sql);
				}
			}
		}
		if(!empty($this->hasmany))
		{
			foreach ($this->hasmany as $model=>$column)
			{
				if(empty($column["ondelete"])||$column["ondelete"]=="no action")
				{
					continue;
				}
				if(!class_exists($model))
				{
					include(MODELDIR.$model.".php");
				}
				$has=new $model();
				if($column["ondelete"]=="cascade")
				{									
					$where=$column["ref_column"]."='".$this->data[$column["own_column"]]."'";
					$has->delete($where);
				}
				else if($column["ondelete"]=="set null")
				{
					$sql.="update ".$model->getTableName()." set ".$column["ref_column"]."=null where ".$column["ref_column"]."='".$this->data[$column["own_column"]]."'";					
					$this->db->execute($sql);
				}
			}
		}
	}
	//返回true表示可以删除，返回false表示不可以删除
	public function checkRestrict()
	{
		if(!empty($this->hasone))
		{
			foreach ($this->hasone as $column)
			{
				if($column["ondelete"]=="restrict")
				{
					return false;
				}
			}
		}
		if(!empty($this->hasmany))
		{
			foreach ($this->hasmany as $column)
			{
				if($column["ondelete"]=="restrict")
				{
					return false;
				}
			}
		}
		return true;
	}
	
	public  function checkInsert()
	{
		return true;
	}
	
	public function checkDelete()
	{
		return true;
	}
	
	public function checkSave()
	{
		return true;
	}
}